
package com.kweative.model.query;

import com.kweative.model.Table;
import com.kweative.model.data.Row;
import com.kweative.model.column.Column;
import com.kweative.model.relation.ISimpleRelation;
import com.kweative.model.relation.IMultiRelation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author henrik
 */
public class Insert<T extends Table> extends Statement<T> implements SetterStatement<T>,NonQueryStatement {
    private Stack<LinkedList<AssignPhrase>> assignPhrases = new Stack<LinkedList<AssignPhrase>>();
    private LinkedList<AssignPhrase> current;
	private LinkedList<Column> cols = new LinkedList<Column>();
    public Insert(T primTable) {
        super(primTable);
        bulk();
    }

    public Insert<T> set(Column<?,T> col,Object value) {
        current.add(new AssignPhrase(col, value, this));
		cols.add(col);
        return this;
    }
    public Insert<T> set(Column<?,T>[] cols,Object[] values) {
        for(int i = 0; i < cols.length;i++) {
            set(cols[i],values[i]);
			this.cols.add(cols[i]);
        }
        return this;
    }
    public Insert<T> set(Map<Column<?,T>,Object> nameValue) {
        for(Column col:nameValue.keySet()) {
            set(col,nameValue.get(col));
			this.cols.add(col);
        }
        return this;
    }
    public Insert<T> from(Row<T> row) {
        for(Field f: row.getClass().getDeclaredFields()) {
            try {
                Object fVal = f.get(row);
                if (fVal == null) continue;
                Column col = null;
                if (fVal instanceof Row) {
                    fVal = ((Row)fVal).__primary;
                    col = getTable().getParentKey(row);
                } else {
                    col = getTable().getColumn(f.getName());
                }
                
                set(col, fVal);
            } catch (Exception ex) {
                Logger.getLogger(Insert.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return this;
    }

    public <C extends Table> Insert<T> add(IMultiRelation<T, C> relation, Row<T> parent, Row<C> child) {
        relation.add(parent,child);
        return this;
    }

    
    public <P extends Table> Insert<T> set(ISimpleRelation<P,T> relation, Row<P> parent) {
        relation.set(this,parent);
        return this;
    }
    public <C extends Table> Insert<T> add(ISimpleRelation<T,C> relation, Row<C> child) {
        relation.add(this,child);
        return this;
    }


    public Insert<T> bulk() {
        current =  new LinkedList<AssignPhrase>();
        assignPhrases.add(current);
        return this;
    }
    
    @Override
    public String compile() {
		Collection<Column> tableCols = getTable().getColumns().values();
		for(Column col: tableCols) {
			if (!this.cols.contains(col)) {
				set(col,null);
			}
		}
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("INSERT INTO %s (",QueryUtil.formatTable(getTable())));
        boolean first = true;
        for(AssignPhrase p: current) {
            if (!first) sb.append(",");
            sb.append(QueryUtil.formatColumn(p.getColumn()));
            first = false;
        }
        sb.append(") VALUES ");
        boolean firstPhrase = true;
        for(LinkedList<AssignPhrase> phrases:assignPhrases) {
            first = true;
            if (!firstPhrase) sb.append(",");
            sb.append("(");
            for(AssignPhrase p: phrases) {
                if (!first) sb.append(",");
				Object value = p.getColumn().beforeInsert(p.getValue());
                sb.append(QueryUtil.formatValue(p.getColumn(),value));
                first = false;
            }
            sb.append(")");
            firstPhrase = false;
        }
        return sb.toString();
    }
}
